//
//  TestSerialisation.m
//  LogicSim
//
//  Created by Jeremy Pereira on 13/04/2012.
//  Copyright (c) 2012 Jeremy Pereira. All rights reserved.
//

#import "TestSerialisation.h"
#import "LGSOrGate.h"
#import "LGSEdge.h"
#import "LGSModule.h"
#import "LGSSplitter.h"
#import "LGSXorGate.h"
#import "LGSAndGate.h"
#import "LGSConnectionContext.h"
#import "LGSName.h"


static NSString* const MODULE_JSON = @""
"{"
"	\"nodeType\" : \"LGSModule\","
"	\"name\" : \"fullAdder\","
"   \"inputs\" : [\"a\", \"b\", \"cin\"],"
"   \"outputs\" : [\"bit\", \"cout\"],"
"   \"nodes\" : ["
"		{"
"			\"nodeType\" : \"LGSXorGate\","
"			\"name\" : \"aXorB\""
"		},"
"		{"
"			\"nodeType\" : \"LGSXorGate\","
"			\"name\" : \"aXorBXorC\""
"		},"
"		{"
"			\"nodeType\" : \"LGSAndGate\","
"			\"name\" : \"aAndB\""
"		},"
"		{"
"			\"nodeType\" : \"LGSAndGate\","
"			\"name\" : \"aXorBAndC\""
"		},"
"		{"
"			\"nodeType\" : \"LGSOrGate\","
"			\"name\" : \"cout\""
"		}"
"   ],"
"	\"connections\" : {"
"		\"aXorBXorC.a\" : \"aXorB.output\","
"		\"aXorBAndC.a\" : \"aXorB.output\","
"		\"cout.a\" : \"aXorBAndC.output\","
"		\"cout.b\" : \"aAndB.output\""
"	},"
"	\"inputMappings\" : {"
"		\"a\" : [ \"aXorB.a\", \"aAndB.a\"],"
"		\"b\" : [ \"aXorB.b\", \"aAndB.b\"],"
"		\"cin\" : [ \"aXorBXorC.b\", \"aXorBAndC.b\"]"
"	},"
"	\"outputMappings\" : {"
"		\"bit\" : \"aXorBXorC.output\","
"		\"cout\" : \"cout.output\""
"	}"
"}";


@interface TestSerialisation()

@end

@implementation TestSerialisation

-(void) testJsonifyOrGate
{
    LGSOrGate* orGate = [[LGSOrGate alloc] init];
    [orGate setName: [LGSName nameWithString: @"or"]];
    NSDictionary* orDict = [orGate jsonify];
    STAssertTrue([[orDict objectForKey: LGS_NODE_TYPE_KEY] isEqual: [orGate className]],
                  @"node type incorrect %@", 
                  orDict);
    STAssertTrue([[orDict objectForKey: LGS_NAME_KEY] isEqual: @"or"],
                 @"label incorrect %@", 
                 orDict); 
    STAssertTrue([NSJSONSerialization isValidJSONObject: orDict],
                 @"Dictionary cannot be converted to JSON, %@", orDict);
}

-(void) testJsonifyModule
{
    NSData* jsonData = [MODULE_JSON dataUsingEncoding: NSUTF8StringEncoding];
    NSError* error = nil;
    id jsonObject = [NSJSONSerialization JSONObjectWithData: jsonData
                                                    options: 0
                                                      error: &error];
    NSAssert1(jsonObject != nil, @"Invalid JSON string, %@", error);
    NSAssert1([jsonObject isKindOfClass: [NSDictionary class]],
              @"Invalid JSON object, %@", jsonObject);
    LGSConnectionContext* connectionContext = [[LGSConnectionContext alloc] init];
    id module = [LGSNode makeNodeFromDictionary: jsonObject
                              connectionContext: connectionContext
                                          error: &error];
    STAssertNotNil(module, @"Failed to deserialise, error %@", error);
    
    NSDictionary* moduleDict = [module jsonify];
    STAssertTrue([[moduleDict objectForKey: LGS_NODE_TYPE_KEY] isEqual: [LGSModule className]],
                 @"node type incorrect %@", 
                 moduleDict);
    NSArray* nodes = [moduleDict objectForKey: LGS_NODES_ARRAY_KEY];
    STAssertNotNil(nodes, @"No nodes in module: %@", moduleDict);
    STAssertTrue([NSJSONSerialization isValidJSONObject: moduleDict], 
                 @"Not valiud JSON: %@", 
                 nodes);
    STAssertNotNil([moduleDict objectForKey: LGS_INPUT_LABELS_KEY], 
                   @"Module has no inputs: %@", 
                   moduleDict);
    STAssertNotNil([moduleDict objectForKey: LGS_OUTPUT_LABELS_KEY], 
                   @"Module has no outputs: %@", 
                   moduleDict);
    NSDictionary* connections = [moduleDict objectForKey: LGS_CONNECTIONS_KEY];
    STAssertNotNil(connections, @"No connections recorded");
    STAssertTrue([connections count] == 4, @"Wrong number of connections %@", connections);
    NSDictionary* inputMappings = [moduleDict objectForKey: LGS_INPUT_MAPPINGS_KEY];
    STAssertNotNil(inputMappings, @"No input mappings recorded");
    STAssertTrue([inputMappings count] == 3,
                 @"Wrong number of input mappings %@",
                 inputMappings);
    NSDictionary* outputMappings = [moduleDict objectForKey: LGS_OUTPUT_MAPPINGS_KEY];
    STAssertNotNil(outputMappings, @"No output mappings recorded");
    STAssertTrue([outputMappings count] == 2,
                 @"Wrong number of output mappings %@",
                 outputMappings);
    jsonData = [NSJSONSerialization dataWithJSONObject: moduleDict
                                               options: NSJSONWritingPrettyPrinted
                                                 error: nil];
    NSString* jsonString = [[NSString alloc] initWithData: jsonData 
                                                 encoding: NSUTF8StringEncoding];

    NSLog(@"Serialised module: %@", jsonString);
}


-(void) testCreateModuleFromString
{
    NSData* jsonData = [MODULE_JSON dataUsingEncoding: NSUTF8StringEncoding];
    NSError* error = nil;
    id jsonObject = [NSJSONSerialization JSONObjectWithData: jsonData
                                                    options: 0
                                                      error: &error];
    NSAssert1(jsonObject != nil, @"Invalid JSON string, %@", error); 
    NSAssert1([jsonObject isKindOfClass: [NSDictionary class]], 
              @"Invalid JSON object, %@", jsonObject); 
    LGSConnectionContext* connectionContext = [[LGSConnectionContext alloc] init];
    id deserialisedNode = [LGSNode makeNodeFromDictionary: jsonObject 
                                        connectionContext: connectionContext 
                                                    error: &error];
    STAssertNotNil(deserialisedNode, @"Failed to deserialise, error %@", error);
    STAssertTrue([deserialisedNode isKindOfClass: [LGSModule class]], 
                 @"Incorrect type %@", 
                 deserialisedNode);
    STAssertNotNil([deserialisedNode nodeForName: [LGSName nameWithString: @"aAndB"]],
                   @"No node named aAndB");
    LGSEdge* anEdge = [[LGSEdge alloc] init];
    [deserialisedNode addInput: anEdge
                       forName: [LGSName nameWithConnectionString:@"a"]];
    STAssertNotNil([anEdge output], 
                   @"Inputs do not work on the module %@", 
                   deserialisedNode);
    anEdge = [[LGSEdge alloc] init];
    [deserialisedNode addOutput: anEdge forNameString: @"bit"];
    STAssertNotNil([anEdge input], 
                   @"Outputs do not work on the module %@", 
                   deserialisedNode);
    STAssertEqualObjects([deserialisedNode name], [LGSName nameWithString: @"fullAdder"],
                          @"Module has incorrect name:  %@", 
                          [deserialisedNode name]);
}

static NSString* const CONNECTING_STRING = @""
"		{"
"   		\"label\" : \"a\","
"   		\"nodeType\" : \"LGSSplitter\","
"   		\"uniqueId\" : 3,"
"   		\"inputs\" : [1],"
"   		\"outputs\" : [2,5]"
"   	}";
static NSString* const NODE_2_STRING = @""
"		{"
"   		\"label\" : \"b\","
"   		\"nodeType\" : \"LGSSplitter\","
"   		\"uniqueId\" : 2,"
"   		\"inputs\" : [3],"
"   		\"outputs\" : [5]"
"   	}";

-(void) testCreateConnection
{
	LGSAndGate* gate1 = [[LGSAndGate alloc] initWithNameString: @"gate1"];
	LGSAndGate* gate2 = [[LGSAndGate alloc] initWithNameString: @"gate2"];
	LGSAndGate* gate3 = [[LGSAndGate alloc] initWithNameString: @"gate3"];
    LGSConnectionContext* context = [[LGSConnectionContext alloc] init];
    [context addNode:gate1 error: nil];
    [context addNode:gate2 error: nil];
    [context addNode:gate3 error: nil];
    
    NSError* error = nil;
    NSDictionary* connections
    	= @{
         	@"gate1.a" : @"gate2.output",
          	@"gate1.b" : @"gate3.output"
          };
    STAssertTrue([LGSNode createConnections: connections
                               usingContext: context
                                      error: &error],
                 @"Failed to create connections %@", error);
    STAssertTrue([[gate1 input: [[gate1 class] inputAName]] input] == gate2,
                 @"gate1.a is wrong %@",
                 [[gate1 input: [[gate1 class] inputAName]] input]);
}

@end
